package com.lzx.hbh_system.shiro;

import com.lzx.hbh_system.dto.UserDto;
import com.lzx.hbh_system.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public  class ReflushToken {
    @Autowired
    private RedisUtils redisUtils;
    private static ReflushToken reflushToken;

    @PostConstruct
    public void init() {
        reflushToken = this;
        redisUtils = this.redisUtils;
    }


    /**
     * 判断并返回新Token方法
     * 什么情况下会刷新token并返回？
     * ※ 只有redis存在用户登陆信息，为登陆状态下，且原token过期情况下。
     * ※ 其余情况返回oldToken
     * eq：新token需要前端再次设置并在下次请求时携带，否则不能正常请求。
     *
     * @param oldToken
     * @return
     */
    public static String getAndCheckToken(String oldToken) {
        //初始化
        //获取当前登陆用户名
        String username = JWTUtil.getUsername(oldToken);
        //判断redis中是否存在该用户信息
        UserDto userDto = (UserDto) reflushToken.redisUtils.getObject(username);
        System.out.println("当前是否存在用户:" + username + "，redis信息：" + userDto);
        //初始化newToken的值
        String newToken = null;
        if (userDto != null) {//redis存在
            String tokenkey = username + "token";
            // 且token也存在
            String token = reflushToken.redisUtils.get(tokenkey);
            System.out.println("redis用户Token为："+token);
            String salt = userDto.getSalt();
            //生成旧的secret 注意 生成的secret的字符加密顺序。。。。
            String secret = Md5Utils.MD5(salt + username);
            if (!ObjectUtils.isEmpty(token)) {
                // 不为空  校验 是否过期
                if (!JWTUtil.verify(token, username, secret)) {
                    // 过期，重新生产token放入redis中，redis存放有效期为20分钟
                    long timeout = 20;
                    newToken = JWTUtil.sign(username, secret);
                    reflushToken.redisUtils.setEx(tokenkey, newToken, timeout, TimeUnit.MINUTES);
                    log.info("登陆用户原Token已失效，重新生成Token，有效期："+timeout+"分钟");
                    return newToken;
                }
                // 未过期
                return token;
            } else {
                // redis 中 不存在该用户token时  且用户信息缓存存在的前提下
                // 校验当前前端传来的token是否过期
                if (JWTUtil.verify(oldToken, username, secret)) {
                    // 没过期
                    long timeout = 20;
                    reflushToken.redisUtils.setEx(tokenkey, oldToken, timeout, TimeUnit.MINUTES);
                    log.info("登陆用户原Token有效，有效期更新，有效期："+timeout+"分钟"+",当前用户Token：["+oldToken+"]");
                    return oldToken;
                }
                // 过期
                return oldToken;
            }
            /*//旧的salt
            String oldsalt =  userDto.getSalt();
            //生成旧的secret 注意 生成的secret的字符加密顺序。。。。
            String oldsecret = Md5Utils.MD5(oldsalt+username);
            //判断该token是否有效。取反
            boolean isOldTokenValid  = false;
            isOldTokenValid = JWTUtil.verify(oldToken,username,oldsecret);//eq:这里其实验证token是否匹配，不是实际意义上的token是否超过有效时间 可能还需要专门的token有效期匹配？？？
            log.info("旧Token是否过期："+!isOldTokenValid);
            int lastTimeout = 0;
            if (userDto.getIslogin() && !isOldTokenValid){//并且为登陆状态，且旧token失效，
                //生成新salt值
                String newSalt = SaltUtil.getSalt(5);
                //更新salt值
                userDto.setSalt(newSalt);
                //获取该key剩余有效期（初始登陆有效期）--时间单位/时
                //问题：获取一次改expire就减1 直到为0 redis就删除了。。。。什么问题？？待解决
                //记录时间： 2022-1-27 01：08
                //可能是redistemplate的底层调用出现问题？，根结暂时还没找到，
                //暂时解决方案是：重新拿到该值+1操作，再进行存放redis中去。
                //问题解决时间：2022-1-27 16：16
                System.out.println(reflushToken.redisUtils.getExpire(username,TimeUnit.HOURS));
                lastTimeout = Math.toIntExact(reflushToken.redisUtils.getExpire(username,TimeUnit.HOURS));
                lastTimeout = lastTimeout + 1;
                System.out.println(lastTimeout);
                System.out.println(reflushToken.redisUtils.getExpire(username,TimeUnit.HOURS));
                //存放回新dto信息
                if (lastTimeout == 0){
                    lastTimeout = 1; // 若无则设置状态有效一小时
                }
                reflushToken.redisUtils.delete(username);
                reflushToken.redisUtils.setEx(username,userDto,lastTimeout,TimeUnit.HOURS);
                UserDto newuserdto = (UserDto) reflushToken.redisUtils.getObject(username);
                System.out.println("新的生成的salt值："+newSalt);
                System.out.println("新的redis中的登陆用户信息salt值："+newuserdto.getSalt());
                //生成新secret 注意MD5字符串拼接的顺序，谁在前谁在后 要统一。。。【统一 salt+name】
                String secret = Md5Utils.MD5(newSalt+username);
                System.out.println("生成的新的secret："+secret);
                //生成新token
                newToken = JWTUtil.sign(username,secret);
                //发送消息
                try {
                    //....这里写推送newToken给前端的代码....
                    //记录解决方案：使用Sse服务进行主动推送消息到指定客户端，
                    //记录时间：2022-1-28 10：06
                    //这里需要将新的Token推送给前端，前端进行保存，并在下次发送请求时携带该新的token进行访问，否则不能访问。
                    SseUtil.sendMessage(username,newToken);
                } catch (Exception e) {
                    //问题记录：调用就报该异常，前端连接无反应，后端调用controller成功，使用SseUtil调用sendOneMsg成功，控制台返回数据。前端无反应，接收不到数据
                    //记录时间：2022-1-28 17：30
                    //未解决
                    log.info(e.getMessage()+e.getCause());
                    throw new AuthenticationException("SSEServiceError");
                }
                log.info("Token已刷新！原Token：["+oldToken+"],新Token：["+newToken+"]");
            }else if(userDto.getIslogin() && isOldTokenValid){//redis存在且登陆状态，且旧token有效，则返回旧token，不进行刷新token操作
                log.info("redis存在且登陆状态，且旧token有效，则返回旧token，不进行刷新token操作");
            }else{
                //否则，redis存在且离线状态，则返回旧token，不进行刷新token操作
                log.info("redis存在且离线状态，不进行刷新token操作");
                throw new AuthenticationException("LOGINOUTEXECEPTION");
            }
            newToken = oldToken;*/
        } else {//若redis中不存在用户登陆信息，则直接返回请求头的token
            log.info("redis中不存在用户登陆信息，则直接返回请求头的原有token");
            return oldToken;
        }
    }
}
